package com.langll.ai.minio.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.langll.ai.minio.constant.DocumentFileVectorState;
import com.langll.ai.minio.mapper.AiDocumentFileMapper;
import com.langll.ai.minio.model.AiDocumentFile;
import com.langll.ai.minio.model.UploadFileDTO;
import com.langll.ai.minio.model.UploadFileVo;
import com.langll.ai.minio.service.IAiDocumentFileService;
import com.langll.ai.minio.service.MinioService;
import com.langll.ai.tika.TikaUtil;
import com.langll.ai.tika.TikaVo;
import com.langll.ai.vector.config.MilvusArchive;
import com.langll.ai.vector.service.MilvusService;
import io.milvus.v2.service.vector.response.InsertResp;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author langll2
 * @since 2025-04-11
 */
@Service
@RequiredArgsConstructor
public class AiDocumentFileServiceImpl extends ServiceImpl<AiDocumentFileMapper, AiDocumentFile> implements IAiDocumentFileService {

    private final OllamaEmbeddingModel ollamaEmbeddingModel;
    private final MilvusService milvusService;
    private final TikaUtil tikaUtil;
    private final MinioService minioService;

    @Override
    public UploadFileVo saveFile(MultipartFile file) {
        try {
            // 上传文件到minio
            UploadFileDTO uploadFileDTO = minioService.uploadFile(file);

            AiDocumentFile aiDocumentFile = new AiDocumentFile();
            aiDocumentFile.setName(file.getOriginalFilename());
            aiDocumentFile.setSize(file.getSize());
            aiDocumentFile.setFileObjectName(uploadFileDTO.getFileObjectName());
            aiDocumentFile.setFileObjectUrl(uploadFileDTO.getFileObjectUrl());
            aiDocumentFile.setVectorState(DocumentFileVectorState.INITIAL);
            this.baseMapper.insert(aiDocumentFile);
            return aiDocumentFile.toUploadFileVo();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<UploadFileVo> listFile() {
        return this.baseMapper.selectList(null)
                .stream()
                .map(AiDocumentFile::toUploadFileVo)
                .collect(Collectors.toList());
    }

    @Override
    public Boolean deleteFile(String fileObjectName) {
        Boolean delete = true;
        try{
            AiDocumentFile aiDocumentFile = this.baseMapper.selectOne(new QueryWrapper<AiDocumentFile>().eq("file_object_name", fileObjectName));
            deleteFile(aiDocumentFile);
        } catch (Exception e) {
            delete = false;
            throw new RuntimeException(e);
        }
        return delete;
    }

    @Override
    public Boolean deleteFile(Serializable id) {
        Boolean delete = true;
        try{
            AiDocumentFile aiDocumentFile = this.baseMapper.selectById(id);
            deleteFile(aiDocumentFile);
        } catch (Exception e) {
            delete = false;
            throw new RuntimeException(e);
        }
        return delete;
    }

    private void deleteFile(AiDocumentFile aiDocumentFile) {
        try{
            //删除 Object对象存储中的文件 Minio
            minioService.deleteFile(aiDocumentFile.getFileObjectName());
            // 删除 向量数据库中的数据
            vectorDeleteByFileName(aiDocumentFile.getFileObjectName());
            //删除mysql数据库中的记录
            this.baseMapper.deleteById(aiDocumentFile.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public UploadFileVo updateFile(String fileObjectName, MultipartFile file) {
        this.deleteFile(fileObjectName);
        return this.saveFile(file);
    }

    @Override
    public UploadFileVo vectorFile(Serializable id) {
        AiDocumentFile aiDocumentFile = this.baseMapper.selectById(id);
        // 获取文件内容
        TikaVo tikaVo = null;
        try {
            tikaVo = tikaUtil.extractText(minioService.downloadFileInputStream(aiDocumentFile.getFileObjectName()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (tikaVo != null && Objects.nonNull(tikaVo.getText())) {
            List<float[]> embedList = new ArrayList<>();
            List<String> textList = tikaVo.getText();
            List<String> metadataList = tikaVo.getMetadata();
            List<String> fileNameList = new ArrayList<>();
            for (String s : tikaVo.getText()) {
                embedList.add(ollamaEmbeddingModel.embed(s));
                fileNameList.add(aiDocumentFile.getFileObjectName());
            }
            InsertResp insertResp = milvusService.batchInsert(embedList, textList, metadataList, fileNameList);
            //更新 VectorState
            aiDocumentFile.setVectorState(DocumentFileVectorState.SUCCESSFUL);
            this.baseMapper.updateById(aiDocumentFile);
        }
        UploadFileVo uploadFileVo = aiDocumentFile.toUploadFileVo();
        return uploadFileVo;
    }

    @Override
    public void vectorDeleteByFileName(String fileObjectName) {
        String filter = String.format("%s == \"%s\"", MilvusArchive.Field.FILE_NAME, fileObjectName);
        milvusService.delete(filter);
    }
    
    @Override
    public UploadFileVo downloadFile(Serializable id){
        AiDocumentFile aiDocumentFile = this.baseMapper.selectById(id);
        return updateAiDocumentFileUrl(aiDocumentFile);
    }

    private UploadFileVo updateAiDocumentFileUrl(AiDocumentFile aiDocumentFile){
        UploadFileVo uploadFileVo = aiDocumentFile.toUploadFileVo();
        String url = null;
        try {
            url = minioService.getPresignedObjectUrl(uploadFileVo.getFileObjectName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        uploadFileVo.setUrl(url);
        aiDocumentFile.setFileObjectUrl(url);
        this.baseMapper.updateById(aiDocumentFile);
        return uploadFileVo;
    }
}
